package com.skyyan.netty.jjf.client;

import com.skyyan.netty.jjf.codec.JjfMessageDecoder;
import com.skyyan.netty.jjf.codec.JjfMessageEncoder;
import com.skyyan.netty.jjf.protocol.*;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.timeout.IdleStateHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Scanner;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;

public class JjfClient {

    private static final Logger log = LoggerFactory.getLogger(JjfClient.class);

    // 客户端配置
    private static final String DEFAULT_HOST = "localhost";
    private static final int DEFAULT_PORT = 8888;
    private static final int WORKER_THREADS = 4;

    // 超时配置（秒）
    private static final int READER_IDLE_TIME = 90;  // 读超时
    private static final int WRITER_IDLE_TIME = 30;  // 写超时
    private static final int ALL_IDLE_TIME = 120;    // 总超时

    // 重连配置
    private static final int MAX_RETRY_COUNT = 10;
    private static final int RETRY_DELAY_SECONDS = 5;

    private final String host;
    private final int port;
    private final byte[] clientAddress;

    private EventLoopGroup workerGroup;
    private ChannelFuture channelFuture;
    private Channel channel;
    private volatile boolean running = false;

    // 用于等待服务器响应的Future
    private CompletableFuture<Message> responseFuture;

    public JjfClient() {
        this(DEFAULT_HOST, DEFAULT_PORT, createDefaultAddress());
    }

    public JjfClient(String host, int port, byte[] clientAddress) {
        this.host = host;
        this.port = port;
        this.clientAddress = clientAddress.clone();
    }

    /**
     * 启动客户端
     */
    public void start() {
        workerGroup = new NioEventLoopGroup(WORKER_THREADS);
        running = true;

        try {
            connectWithRetry();
        } catch (Exception e) {
            log.error("客户端启动失败", e);
            shutdown();
        }
    }

    /**
     * 带重试的连接方法
     */
    private void connectWithRetry() {
        int retryCount = 0;

        while (running && retryCount < MAX_RETRY_COUNT) {
            try {
                if (connect()) {
                    log.info("客户端连接成功");
                    channel = channelFuture.channel();
                    // 启动交互式命令处理
                    startInteractiveMode();
                    // 等待连接关闭
                    channelFuture.channel().closeFuture().sync();

                    if (running) {
                        log.warn("连接意外断开，准备重连...");
                        retryCount++;
                        if (retryCount < MAX_RETRY_COUNT) {
                            Thread.sleep(RETRY_DELAY_SECONDS * 1000);
                        }
                    }
                } else {
                    retryCount++;
                    if (retryCount < MAX_RETRY_COUNT) {
                        log.warn("连接失败，{}秒后重试 ({}/{})",
                                RETRY_DELAY_SECONDS, retryCount, MAX_RETRY_COUNT);
                        Thread.sleep(RETRY_DELAY_SECONDS * 1000);
                    }
                }
            } catch (InterruptedException e) {
                log.info("客户端被中断");
                Thread.currentThread().interrupt();
                break;
            } catch (Exception e) {
                log.error("连接过程中发生异常", e);
                retryCount++;
                if (retryCount < MAX_RETRY_COUNT) {
                    try {
                        Thread.sleep(RETRY_DELAY_SECONDS * 1000);
                    } catch (InterruptedException ie) {
                        Thread.currentThread().interrupt();
                        break;
                    }
                }
            }
        }

        if (retryCount >= MAX_RETRY_COUNT) {
            log.error("达到最大重试次数，客户端启动失败");
        }

        shutdown();
    }

    /**
     * 执行单次连接
     */
    private boolean connect() throws InterruptedException {
        Bootstrap bootstrap = new Bootstrap();
        bootstrap.group(workerGroup)
                .channel(NioSocketChannel.class)
                .option(ChannelOption.SO_KEEPALIVE, true)
                .option(ChannelOption.TCP_NODELAY, true)
                .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 10000)
                .handler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    protected void initChannel(SocketChannel ch) throws Exception {
                        ch.pipeline()
                                // 日志处理器（可选）
                                .addLast("logger", new LoggingHandler(LogLevel.DEBUG))

                                // 空闲状态检测
                                .addLast("idleStateHandler", new IdleStateHandler(
                                        READER_IDLE_TIME, WRITER_IDLE_TIME, ALL_IDLE_TIME, TimeUnit.SECONDS))

                                // 协议编解码器
                                .addLast("decoder", new JjfMessageDecoder())
                                .addLast("encoder", new JjfMessageEncoder())

                                // 业务处理器
                                .addLast("clientHandler", new ClientHandler(clientAddress));
                    }
                });

        try {
            channelFuture = bootstrap.connect(host, port).sync();
            return channelFuture.isSuccess();
        } catch (Exception e) {
            log.error("连接到 {}:{} 失败", host, port, e);
            return false;
        }
    }

    /**
     * 启动交互式命令处理
     */
    private void startInteractiveMode() {
        new Thread(() -> {
            Scanner scanner = new Scanner(System.in);
            System.out.println("=== 交互式客户端 ===");
            System.out.println("请输入命令:");
            System.out.println("1. 发送注册报文");
            System.out.println("2. 发送注册确认报文");
            System.out.println("3. 发送余额查询报文");
            System.out.println("4. 发送充值报文");
            System.out.println("5. 发送开卡报文");
            System.out.println("6. 发送阶梯水电价查询报文");
            System.out.println("quit. 退出");
            System.out.println("==================");

            while (true) {
                System.out.print("请输入命令: ");
                String command = scanner.nextLine().trim();

                if ("quit".equalsIgnoreCase(command)) {
                    System.out.println("正在退出...");
                    channel.close();
                    break;
                }

                try {
                    switch (command) {
                        case "1":
                            sendRegisterMessage();
                            break;
                        case "2":
                            sendRegisterConfirmMessage();
                            break;
                        case "3":
                            sendBalanceQueryMessage();
                            break;
                        case "4":
                            sendRechargeMessage();
                            break;
                        case "5":
                            sendOpenCardMessage();
                            break;
                        case "6":
                            sendWaterElectricityPriceQueryMessage();
                            break;
                        default:
                            System.out.println("未知命令，请重新输入");
                            break;
                    }
                } catch (Exception e) {
                    System.err.println("执行命令时发生错误: " + e.getMessage());
                    e.printStackTrace();
                }
            }
        }).start();
    }

    /**
     * 发送注册报文
     */
    public void sendRegisterMessage() {
        System.out.println("发送注册报文...");
        RegisterMessage.Uplink registerMsg = new RegisterMessage.Uplink(clientAddress);
        sendMessageAndWaitForResponse(registerMsg, "注册");
    }

    /**
     * 发送注册确认报文
     */
    public void sendRegisterConfirmMessage() {
        System.out.println("发送注册确认报文...");
        // 示例数据
        byte[] deviceRegId = new byte[16];
        for (int i = 0; i < 16; i++) {
            deviceRegId[i] = (byte) (i + 1);
        }

        RegisterConfirmMessage.Uplink confirmMsg = new RegisterConfirmMessage.Uplink(
                clientAddress, deviceRegId);
        sendMessageAndWaitForResponse(confirmMsg, "注册确认");
    }

    /**
     * 发送余额查询报文
     */
    public void sendBalanceQueryMessage() {
        System.out.println("发送余额查询报文...");
        // 示例数据
        byte[] deviceRegId = new byte[16];
        for (int i = 0; i < 16; i++) {
            deviceRegId[i] = (byte) (i + 1);
        }
        int deviceSerialNo = 0x123456;
        byte[] farmerRegId = new byte[16];
        for (int i = 0; i < 16; i++) {
            farmerRegId[i] = (byte) (i + 10);
        }

        BalanceQueryMessage.Uplink queryMsg = new BalanceQueryMessage.Uplink(
                deviceRegId, deviceSerialNo, farmerRegId);
        sendMessageAndWaitForResponse(queryMsg, "余额查询");
    }

    /**
     * 发送充值报文
     */
    public void sendRechargeMessage() {
        System.out.println("发送充值报文...");
        // 示例数据
        byte[] deviceRegId = new byte[16];
        for (int i = 0; i < 16; i++) {
            deviceRegId[i] = (byte) (i + 1);
        }
        int deviceSerialNo = 0x123456;
        byte[] farmerRegId = new byte[16];
        for (int i = 0; i < 16; i++) {
            farmerRegId[i] = (byte) (i + 10);
        }
        int rechargeAmount = 10000; // 100元
        String orderId = "ORDER12345678";

        RechargeMessage.Uplink rechargeMsg = new RechargeMessage.Uplink(
                deviceRegId, deviceSerialNo, farmerRegId, rechargeAmount, orderId);
        sendMessageAndWaitForResponse(rechargeMsg, "充值");
    }

    /**
     * 发送开卡报文
     */
    public void sendOpenCardMessage() {
        System.out.println("发送开卡报文...");
        // 示例数据
        String name = "张三";
        String phone = "13812345678";
        String idCard = "110101199001011234";
        int areaCode = 1234;
        byte[] cardNo = new byte[]{0x01, 0x02, 0x03, 0x04, 0x05};

        // 创建19字节地址域 (16字节设备注册号 + 3字节管理机序列号)
        byte[] openCardAddress = createOpenCardAddress();

        OpenCardMessage.Uplink openCardMsg = new OpenCardMessage.Uplink(
                openCardAddress,  name, phone, idCard, areaCode, cardNo);
        sendMessageAndWaitForResponse(openCardMsg, "开卡");
    }

    /**
     * 发送阶梯水电价查询报文
     */
    public void sendWaterElectricityPriceQueryMessage() {
        System.out.println("发送阶梯水电价查询报文...");
        // 示例数据
        byte[] deviceRegId = new byte[16];
        for (int i = 0; i < 16; i++) {
            deviceRegId[i] = (byte) (i + 1);
        }
        int deviceSerialNo = 0x1563456;
        WaterElectricityPriceQueryMessage.Uplink priceQueryMsg =
                new WaterElectricityPriceQueryMessage.Uplink(deviceRegId,deviceSerialNo);
        sendMessageAndWaitForResponse(priceQueryMsg, "阶梯水电价查询");
    }

    /**
     * 发送消息并等待服务器响应
     *
     * @param message 消息
     * @param type    消息类型
     */
    private void sendMessageAndWaitForResponse(Message message, String type) {
        responseFuture = new CompletableFuture<>();
        channel.writeAndFlush(message);

        try {
            // 等待响应，最多等待10秒
            Message response = responseFuture.get(10, TimeUnit.SECONDS);
            System.out.println("收到" + type + "响应: " + response);
            parseResponseMessage(response);
        } catch (Exception e) {
            System.err.println("等待" + type + "响应超时或发生错误: " + e.getMessage());
        }
    }

    /**
     * 解析服务器返回的响应报文
     *
     * @param msg 响应报文
     */
    private void parseResponseMessage(Message msg) {
        System.out.println("=== 解析响应报文 ===");
        System.out.println("报文类型: " + msg.getClass().getSimpleName());
        System.out.println("控制域: 0x" + String.format("%02X", msg.getControl()));
        System.out.println("AFN: 0x" + String.format("%02X", msg.getAfn()));
        System.out.println("地址域: " + (msg.getAddress() != null ? cn.hutool.core.util.HexUtil.encodeHexStr(msg.getAddress()) : "null"));

        if (msg instanceof RegisterMessage) {
            parseRegisterMessage((RegisterMessage) msg);
        } else if (msg instanceof RegisterConfirmMessage) {
            parseRegisterConfirmMessage((RegisterConfirmMessage) msg);
        } else if (msg instanceof BalanceQueryMessage) {
            parseBalanceQueryMessage((BalanceQueryMessage) msg);
        } else if (msg instanceof RechargeMessage) {
            parseRechargeMessage((RechargeMessage) msg);
        } else if (msg instanceof OpenCardMessage) {
            parseOpenCardMessage((OpenCardMessage) msg);
        } else if (msg instanceof WaterElectricityPriceQueryMessage) {
            parseWaterElectricityPriceQueryMessage((WaterElectricityPriceQueryMessage) msg);
        } else {
            System.out.println("未知报文类型");
        }
        System.out.println("==================");
    }

    /**
     * 解析注册报文
     */
    private void parseRegisterMessage(RegisterMessage msg) {
        if (msg.getControl() == com.skyyan.netty.jjf.protocol.FrameConstants.CONTROL_DOWNLINK) {
            System.out.println("==注册下行报文=======");
            if (msg instanceof RegisterMessage.Downlink) {
                RegisterMessage.Downlink downlink = (RegisterMessage.Downlink) msg;
                System.out.println("注册下行报文内容: " + downlink.toString());
                System.out.println("设备注册号: " + cn.hutool.core.util.HexUtil.encodeHexStr(downlink.getDeviceRegId()));
            }
        } else {
            System.out.println("==注册上行报文=======");
            if (msg instanceof RegisterMessage.Uplink) {
                RegisterMessage.Uplink uplink = (RegisterMessage.Uplink) msg;
                System.out.println("注册上行报文内容: " + uplink.toString());
            }
        }
    }

    /**
     * 解析注册确认报文
     */
    private void parseRegisterConfirmMessage(RegisterConfirmMessage msg) {
        if (msg.getControl() == com.skyyan.netty.jjf.protocol.FrameConstants.CONTROL_DOWNLINK) {
            System.out.println("==注册确认下行报文=======");
            if (msg instanceof RegisterConfirmMessage.Downlink) {
                RegisterConfirmMessage.Downlink downlink = (RegisterConfirmMessage.Downlink) msg;
                System.out.println("注册确认下行报文内容: " + downlink.toString());
            }
        } else {
            System.out.println("==注册确认上行报文=======");
            if (msg instanceof RegisterConfirmMessage.Uplink) {
                RegisterConfirmMessage.Uplink uplink = (RegisterConfirmMessage.Uplink) msg;
                System.out.println("注册确认上行报文内容: " + uplink.toString());
            }
        }
    }

    /**
     * 解析余额查询报文
     */
    private void parseBalanceQueryMessage(BalanceQueryMessage msg) {
        System.out.println("收到余额查询响应报文");
        if (msg instanceof BalanceQueryMessage.Downlink) {
            System.out.println("==余额查询下行报文=======");
            BalanceQueryMessage.Downlink downlink = (BalanceQueryMessage.Downlink) msg;
            System.out.println("余额查询下行报文内容: " + downlink.toString());
            System.out.println("状态码: 0x" + String.format("%02X", downlink.getStatusCodeBCD()));
            System.out.println("余额: " + downlink.getMoneyValue() + "分");
            System.out.println("用户名: " + downlink.getUsername());
            System.out.println("卡号: " + downlink.getCardNO());
        } else if (msg instanceof BalanceQueryMessage.Uplink) {
            System.out.println("==余额查询上行报文=======");
            BalanceQueryMessage.Uplink uplink = (BalanceQueryMessage.Uplink) msg;
            System.out.println("余额查询上行报文内容: " + uplink.toString());
        } else {
            System.out.println("==余额查询未知类型报文=======");
            System.out.println("报文类型: " + msg.getClass().getName());
        }
    }

    /**
     * 解析充值报文
     */
    private void parseRechargeMessage(RechargeMessage msg) {
        System.out.println("收到充值报文");
        if (msg instanceof RechargeMessage.Downlink) {
            System.out.println("==充值下行报文=======");
            RechargeMessage.Downlink downlink = (RechargeMessage.Downlink) msg;
            System.out.println("充值下行报文内容: " + downlink.toString());
            System.out.println("状态码: 0x" + String.format("%02X", downlink.getStatusCodeBCD()));
            System.out.println("充值金额: " + downlink.getBalance() + "分");
            System.out.println("交易流水号: " + downlink.getOrderNoString());
        } else if (msg instanceof RechargeMessage.Uplink) {
            System.out.println("==充值上行报文=======");
            RechargeMessage.Uplink uplink = (RechargeMessage.Uplink) msg;
            System.out.println("充值上行报文内容: " + uplink.toString());
        } else {
            System.out.println("==充值未知类型报文=======");
            System.out.println("报文类型: " + msg.getClass().getName());
        }
    }

    /**
     * 解析开卡报文
     */
    private void parseOpenCardMessage(OpenCardMessage msg) {
        System.out.println("----开卡报文解析------");
        if (msg instanceof OpenCardMessage.Downlink) {
            System.out.println("==开卡下行报文=======");
            OpenCardMessage.Downlink downlink = (OpenCardMessage.Downlink) msg;
            System.out.println("开卡下行报文内容: " + downlink.toString());
            System.out.println("状态码: 0x" + String.format("%02X", downlink.getStatus()));
            System.out.println("订单号: " + downlink.getOrderId());
            System.out.println("卡号: " + cn.hutool.core.util.HexUtil.encodeHexStr(downlink.getFarmerId()));
            System.out.println("状态描述: " + downlink.getStatusDesc());
        } else if (msg instanceof OpenCardMessage.Uplink) {
            System.out.println("==开卡上行报文=======");
            OpenCardMessage.Uplink uplink = (OpenCardMessage.Uplink) msg;
            System.out.println("开卡上行报文内容: " + uplink.toString());
        } else {
            System.out.println("==开卡未知类型报文=======");
            System.out.println("报文类型: " + msg.getClass().getName());
        }
    }

    /**
     * 解析阶梯水电价查询报文
     */
    private void parseWaterElectricityPriceQueryMessage(WaterElectricityPriceQueryMessage msg) {
        System.out.println("----阶梯水电价查询报文解析------");
        if (msg instanceof WaterElectricityPriceQueryMessage.Downlink) {
            System.out.println("==阶梯水电价查询下行报文=======");
            WaterElectricityPriceQueryMessage.Downlink downlink = (WaterElectricityPriceQueryMessage.Downlink) msg;
            System.out.println("阶梯水电价查询下行报文内容: " + downlink.toString());
            System.out.println("状态码: 0x" + String.format("%02X", downlink.getStatusCodeBCD()));
            System.out.println("1阶水价: " + String.format("%.3f", downlink.getWaterPrice1Value() / 1000.0) + "元");
            System.out.println("2阶水价: " + String.format("%.3f", downlink.getWaterPrice2Value() / 1000.0) + "元");
            System.out.println("3阶水价: " + String.format("%.3f", downlink.getWaterPrice3Value() / 1000.0) + "元");
            System.out.println("1阶电价: " + String.format("%.3f", downlink.getTieredPrice1Value() / 1000.0) + "元");
            System.out.println("2阶电价: " + String.format("%.3f", downlink.getTieredPrice2Value() / 1000.0) + "元");
            System.out.println("3阶电价: " + String.format("%.3f", downlink.getTieredPrice3Value() / 1000.0) + "元");
        } else if (msg instanceof WaterElectricityPriceQueryMessage.Uplink) {
            System.out.println("==阶梯水电价查询上行报文=======");
            WaterElectricityPriceQueryMessage.Uplink uplink = (WaterElectricityPriceQueryMessage.Uplink) msg;
            System.out.println("阶梯水电价查询上行报文内容: " + uplink.toString());
        } else {
            System.out.println("==阶梯水电价查询未知类型报文=======");
            System.out.println("报文类型: " + msg.getClass().getName());
        }
    }

    /**
     * 优雅关闭客户端
     */
    public void shutdown() {
        log.info("正在关闭JJF协议客户端...");
        running = false;

        try {
            // 关闭连接
            if (channelFuture != null && channelFuture.channel().isActive()) {
                channelFuture.channel().close().sync();
            }
        } catch (InterruptedException e) {
            log.warn("关闭连接时被中断", e);
            Thread.currentThread().interrupt();
        }

        // 关闭EventLoopGroup
        if (workerGroup != null) {
            workerGroup.shutdownGracefully();
        }

        log.info("JJF协议客户端已关闭");
    }

    /**
     * 检查客户端是否正在运行
     */
    public boolean isRunning() {
        return running && channelFuture != null && channelFuture.channel().isActive();
    }

    /**
     * 创建默认的客户端地址
     * 示例：河南省郑州市中牟县官渡镇邵岗村委会 + 充值机序列号1
     */
    private static byte[] createDefaultAddress() {
        // 行政区划码 (BCD): 41 01 22 (河南省郑州市中牟县)
        // 镇村编码 (BCD): 10 22 01 (官渡镇邵岗村委会)
        // 测站编码 (BIN, 3字节, 小端): 1
        return RegisterMessage.createAddress(
                (short) 0x4101,  // 省市代码
                (byte) 0x22,     // 县代码
                new byte[]{(byte) 0x10, (byte) 0x22, (byte) 0x01}, // 镇村代码
                3111321                // 手持终端序设备序列号
        );
    }

    /**
     * 创建开卡报文所需的19字节地址域
     * @return 19字节地址域 (16字节设备注册号 + 3字节管理机序列号)
     */
    private byte[] createOpenCardAddress() {
        // 前16字节：设备注册号（示例数据）
        byte[] deviceRegId = new byte[16];
        for (int i = 0; i < 16; i++) {
            deviceRegId[i] = (byte) (i + 1);
        }

        // 后3字节：管理机序列号（示例数据）
        byte[] deviceSerialNo = new byte[]{0x01, 0x02, 0x03};

        // 组合成19字节地址域
        byte[] address = new byte[19];
        System.arraycopy(deviceRegId, 0, address, 0, 16);
        System.arraycopy(deviceSerialNo, 0, address, 16, 3);

        return address;
    }

    /**
     * 主方法，用于启动客户端
     */
    public static void main(String[] args) {
        // 解析命令行参数
        String host = DEFAULT_HOST;
        int port = DEFAULT_PORT;
        host="127.0.0.1";

        port=1883;
        if (args.length >= 1) {
            host = args[0];
        }
        if (args.length >= 2) {
            try {
                port = Integer.parseInt(args[1]);
                if (port < 1 || port > 65535) {
                    throw new IllegalArgumentException("端口号必须在1-65535之间");
                }
            } catch (NumberFormatException e) {
                log.error("无效的端口号: {}", args[1]);
                System.exit(1);
            }
        }

        // 创建客户端实例
        final JjfClient client = new JjfClient(host, port, createDefaultAddress());

        // 添加JVM关闭钩子
        Runtime.getRuntime().addShutdownHook(new Thread(() -> {
            log.info("收到关闭信号，正在优雅关闭客户端...");
            client.shutdown();
        }));

        // 启动客户端
        log.info("启动JJF协议客户端，连接到 {}:{}", host, port);
        client.start();
    }
}
